package com.lry.sso.service.ddd.service.impl;


import com.lry.sso.auth.common.dto.AuthConfigInfo;
import com.lry.sso.data.dao.SysPermissionMapper;
import com.lry.sso.data.dao.SysRoleMapper;
import com.lry.sso.data.dao.SysUserMapper;
import com.lry.sso.service.ddd.feign.AuthFeign;
import com.lry.sso.service.ddd.service.CaptchaService;
import com.lry.sso.service.ddd.service.LoginService;
import com.lry.sso.service.redis.service.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 登录服务接口实现
 *
 * @author 李瑞益
 * @date 2019/11/30
 */
@Service
public class LoginServiceImpl implements LoginService {

    private static final Logger logger = LoggerFactory.getLogger(LoginServiceImpl.class);

    @Autowired
    private SysUserMapper sysUserMapper;

    @Autowired
    private SysRoleMapper sysRoleMapper;

    @Autowired
    private SysPermissionMapper sysPermissionMapper;

    @Autowired
    private CaptchaService captchaService;

    @Autowired
    private AuthFeign authFeign;

    @Autowired
    private RedisService redisService;
    
    private AuthConfigInfo authConfigInfo;

    @Override
    public ResultData<LoginResponseVo> login(LoginParamVo param, HttpServletResponse response) {
        
        authConfigInfo = authFeign.getAuthConfig();
        
//        this.validNull(param);
        //验证码
        //todo 暂时关闭验证码
//        if (!captchaService.validCaptcha(param.getCaptchaId(), param.getCaptcha())) {
//            throw new SysInvocationException(SsoErrorEnum.CAPTCHA_ERROR);
//        }
//        captchaService.deleteCaptcha(param.getCaptchaId());
//        this.checkLock(param.getUserId());
        SysUser user = sysUserMapper.getByUsername(param.getUsername());
        logger.info("登录用户：{}",param.getUsername());

        if (user == null) {
            throw new SysInvocationException(LoginError.USERNAME_PASSWORD);
        }
        if (user.getStatus() != null && user.getStatus() == 0) {
            throw new SysInvocationException(LoginError.USER_DISABLE);
        }

        String decryptPassword = authFeign.parsePassword(param.getPassword());
        if(decryptPassword == null){
            logger.info("密码解密失败");
            throw new SysInvocationException(LoginError.USERNAME_PASSWORD);
        }
        logger.info("用户输入的密码：{}", decryptPassword);

        if (!validPassword(decryptPassword, user.getPassword())) {
            this.validErrorLogin(user.getId().toString());
        }

        //登录校验通过后，清除错误登录记录
//        this.clearErrorLoginRecord(user.getUserId());
        String msg = LoginUtil.validDegreePassword(decryptPassword);
        //强制改密
//        if (!StringUtils.isEmpty(msg)) {
//            LoginResponseVo result = new LoginResponseVo();
//            result.setForce(true);
//            return Ret.data(result);
//        }
        SessionInfo info = this.buildSessionInfo(user);
        info.setSessionId(UUID.randomUUID().toString());

        this.addSession(info,response);

        return Ret.data(getResponseBasicUser(info));
    }

    @Override
    public LoginResponseVo getResponseBasicUser(SessionInfo info) {
        String token;
        LoginResponseVo result = new LoginResponseVo();
        BasicUserVo userInfo = new BasicUserVo();

        try {
            token = authFeign.makeToken(info);
        } catch (Exception e) {
            logger.error("用户认证token生成失败:[{}]", e.getMessage());
            throw new SysInvocationException(LoginError.LOGIN_FAIL);
        }
        this.saveTokenToRedis(info.getUserId().toString(), token);

        userInfo.setUserId(info.getUserId());
        userInfo.setUsername(info.getUserName());

        result.setToken(token);
        result.setUserInfo(userInfo);
        return result;
    }

    @Override
    public void logout() {
        SessionInfo sessionInfo = RequestInfoContext.getSession();
        String key = AuthUtil.getLoginJwtKey(sessionInfo.getSessionId());
        String s = redisService.get(key);
        if (!StringUtils.isEmpty(s)) {
            //不为空则情况
            redisService.delete(key);
            logger.info("用户：{}，退出登录", sessionInfo.getUserName());
        }
    }

    /**
     * 登录成功后清除错误登录次数
     *
     * @param userId 用户名
     */
    private void clearErrorLoginRecord(String userId) {
        logger.info("登录成功，清除错误登录记录。。。");
        String pwdKey = AuthUtil.getLoginPwdTimesKey(userId);
        String s = redisService.get(pwdKey);
        if (!StringUtils.isEmpty(s)) {
            redisService.delete(pwdKey);
        }
        logger.info("清除错误密码登录5次记录成功！");
        String ipKey = AuthUtil.getLoginIpTimesKey(userId);
        String ip = redisService.get(ipKey);
        if (!StringUtils.isEmpty(ip)) {
            redisService.delete(ipKey);
        }
        logger.info("清除错误登录30次记录成功！");

    }

    /**
     * 检查 账户与IP锁定状态
     *
     * @param userId 用户名
     */
    private void checkLock(String userId) {
        String ipKey = AuthUtil.getLoginIpTimesKey(userId);
        String ipValue = redisService.get(ipKey);
        if (!StringUtils.isEmpty(ipValue)) {
            int times = Integer.parseInt(ipValue);
            if (times > authConfigInfo.getIpLockTimes() - 1) {
                Long ipPttl = redisService.pttl(ipKey);
                logger.info("同一IP时间范围内错误登录{}次，IP锁定！,锁定key：{},剩余时间：{}分钟", authConfigInfo.getIpLockTimes(), ipKey, ipPttl / 60000.0);
                throw new SysInvocationException(LoginError.IP_LOCK.getCode(), "IP被锁定");
            }
        }
        String pwdKey = AuthUtil.getLoginPwdTimesKey(userId);
        String pwdValue = redisService.get(pwdKey);
        if (!StringUtils.isEmpty(pwdValue)) {
            int times = Integer.parseInt(pwdValue);
            if (times > authConfigInfo.getPwdLockTimes() - 1) {
                Long pwdPttl = redisService.pttl(pwdKey);
                logger.info("密码输入错误{}次以上，锁定账号！,锁定key：{},剩余时间：{}分钟", authConfigInfo.getPwdLockTimes(), pwdKey, pwdPttl / 60000.0);
                throw new SysInvocationException(LoginError.USER_LOCK.getCode(), "用户已被锁定");
            }
        }
    }

    /**
     * 构建需要存储的用户信息对象
     *
     * @param user 读取的用户信息，一定包含userId和userName
     * @return sessioninfo对象
     */
    private SessionInfo buildSessionInfo(SysUser user) {
        SessionInfo sessionInfo = new SessionInfo(user.getId(), user.getUsername());
        List<SysRole> roleList = sysRoleMapper.listByUserId(user.getId());
        List<Integer> roleIdList = roleList.stream().map(SysRole::getId).collect(Collectors.toList());
        sessionInfo.setRoleIdList(roleIdList);
        long count = roleList.stream().filter(c -> c.getId() == 1).count();
        if (count > 0) {
            sessionInfo.setIsAdmin(true);
        }

        List<Integer> permissionIdList = null;
        if (roleIdList != null && roleIdList.size() > 0) {
            List<SysPermission> permissionList = sysPermissionMapper.listByRoleId(roleIdList);
            if (permissionList != null && permissionList.size() > 0) {
                permissionIdList = permissionList.stream().map(SysPermission::getId).collect(Collectors.toList());
            }
        }
        sessionInfo.setPermissionIdList(permissionIdList);
        return sessionInfo;
    }

    /**
     * 保存token到redis；并判断多点登录和注销
     *
     * @param userId 用户id
     * @param token
     */
    private void saveTokenToRedis(String userId, String token) {
        //存入redis 用于注销
        String key = AuthUtil.getLoginJwtKey(userId);
        redisService.set(key, token, authConfigInfo.getLoginExpire());
        logger.info("登录成功，存储用户信息；登录过期时间:{}秒", authConfigInfo.getLoginExpire());
    }

    /**
     * 验证密码
     *
     * @param inputPassword 输入的密码
     * @param password      数据保存的密码
     * @return true：验证成功
     */
    private boolean validPassword(String inputPassword, String password) {
        return password.equals(DigestUtils.md5DigestAsHex(inputPassword.getBytes()));
    }

    /**
     * 验证错误登录
     *
     * @param userId 用户名
     */
    private void validErrorLogin(String userId) {
        //5次锁定
        Integer laveTimes = this.validLoginTimes(userId);
        throw new SysInvocationException(LoginError.USERNAME_PASSWORD.getCode(), "用户密码输入错误，还有" + laveTimes + "机会");
    }

    /**
     * 密码错误次数校验
     *
     * @param userId 用户名
     */
    private Integer validLoginTimes(String userId) {
        String key = AuthUtil.getLoginPwdTimesKey(userId);
        String s = redisService.get(key);
        if (!StringUtils.isEmpty(s)) {
            int times = Integer.parseInt(s);
            if (times > authConfigInfo.getPwdLockTimes() - 1) {
                //超过可尝试测试；重设时间
                logger.info("密码输入错误{}次以上，执行锁定账号！,锁定key：{}", authConfigInfo.getPwdLockTimes(), key);
                redisService.expire(key, authConfigInfo.getPwdLock());

                //ip锁定
                this.validIpLock(userId);
                throw new SysInvocationException(LoginError.USER_LOCK.getCode(), "用户已被锁定，" + authConfigInfo.getPwdLock() / 60 + "分钟后重试");
            } else {
                logger.info("密码输入错误，错误次数+1。。。;操作key：{}", key);
                redisService.setrange(key, String.valueOf(times + 1));
                //ip锁定
                this.validIpLock(userId);
                return authConfigInfo.getPwdLockTimes() - times;
            }
        } else {
            logger.info("用户登录，设置过期时间,key:{},value:{}", key, authConfigInfo.getLoginExpire());
            redisService.set(key, String.valueOf(1), authConfigInfo.getLoginExpire());
            this.validIpLock(userId);
            return authConfigInfo.getPwdLockTimes();
        }
    }

    /**
     * ip锁定判断
     *
     * @param userId 用户名
     * @return 剩余可操作次数
     */
    private Integer validIpLock(String userId) {
        String key = AuthUtil.getLoginIpTimesKey(userId);
        String s = redisService.get(key);
        if (!StringUtils.isEmpty(s)) {
            int times = Integer.parseInt(s);
            if (times > authConfigInfo.getIpLockTimes() - 1) {
                //超过可尝试测试；重设时间
                logger.info("同一IP时间范围内错误登录{}次，执行锁定IP锁定！,锁定key：{}", authConfigInfo.getIpLockTimes(), key);
                redisService.expire(key, authConfigInfo.getIpLock());
                throw new SysInvocationException(LoginError.USER_LOCK.getCode(), "IP被锁定，" + authConfigInfo.getIpLock() / 60 + "分钟后重试");
            } else {
                logger.info("错误登录，错误次数+1。。。;操作key：{}", key);
                redisService.setrange(key, String.valueOf(times + 1));
                return authConfigInfo.getIpLockTimes() - times;
            }
        } else {
            redisService.set(key, String.valueOf(1), authConfigInfo.getLoginExpire());
            return authConfigInfo.getIpLock();
        }
    }
    /**
     * 存储session信息到redis
     * @param info
     */
    private void addSession(SessionInfo info,HttpServletResponse response) {
        Cookie sessionCookie = new Cookie(LoginAuthorizationConstant.SESSION_ID_KEY,info.getSessionId());
        sessionCookie.setPath("/");
        sessionCookie.setDomain("localhost");
        response.addCookie(sessionCookie);
    }
    /**
     * 登录信息为空验证
     *
     * @param param 登录信息
     * @return
     */
    private boolean validNull(LoginParamVo param) {
        if (param == null) {
            throw new SysInvocationException(SysErrorEnum.BUS_INPUT_PARAM);
        }
        if (StringUtils.isEmpty(param.getUsername())) {
            throw new SysInvocationException(LoginError.USERNAME_NULL);
        }
        if (StringUtils.isEmpty(param.getPassword())) {
            throw new SysInvocationException(LoginError.PASSWORD_NULL);
        }
        if (StringUtils.isEmpty(param.getCaptcha())) {
            throw new SysInvocationException(LoginError.CAPTCHA_NULL);
        }
        return true;
    }
}
